package site.leiwa._01_单例模式;


import site.leiwa.common.Safe;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;


@Safe
public class _02Singleton {

    /**
     * singleton实例使用了volatile关键字修饰
     * 主要是避免在多线程环境下由于编译器进行的重排序操作而导致的线程安全问题。
     * JVM在创建一个对象时会进行以下步骤：
     *      1）分配对象内存空间；
     *      2）初始化对象；
     *      3）设置singleton指向分配的内存地址；
     * 编译器为了优化性能，可能会将2、3操作调换顺序，
     * 假设A线程在执行new Singleton()方法时，由于2、3操作重排序，
     * 而初始化对象操作尚未完成时释放了锁。线程B获取锁之后会发现singleton已经不为空，
     * 当线程B获取到singleton对象后如果直接使用就会出错，原因就是对象没有进行初始化操作。
     * 而volatile关键字能避免重排序，因此能保证线程安全
     */
    @SuppressWarnings("LanguageDetectionInspection")
    private volatile static _02Singleton singleton;

    private _02Singleton() {
    }

    public static _02Singleton getInstance() {
        if (singleton == null) {
            synchronized (_02Singleton.class){
                if (singleton == null) {
                    singleton = new _02Singleton();
                }
            }
        }
        return singleton;
    }

    // 验证是否安全
    public static void main(String[] args) throws InterruptedException {
        ArrayList<String> list = new ArrayList<>(200);
        ArrayList<Thread> threads = new ArrayList<>(200);
        for (int i = 0; i < 100; i++) {
            Thread t = new Thread(() -> {
                try {
                    Thread.sleep(1500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                list.add(_02Singleton.getInstance().toString());
            });
            threads.add(t);
        }
        threads.forEach(Thread::start);
        Thread.sleep(2500);
        Set<String> set = new HashSet<>();
        boolean b = set.addAll(list);
        System.out.println(set.size());
    }
}